#include <cstdlib>
#include <cstring>
#include <iostream>
#include <fstream>
#include <glog/logging.h>

#include "reader.h"
#include "writer.h"
#include "value.h"
#include "ji_utils.h"
#include "SampleAlgorithmMix.h"


#define JSON_ALERT_FLAG_KEY ("is_alert")
#define JSON_ALERT_FLAG_TRUE true
#define JSON_ALERT_FLAG_FALSE false


SampleAlgorithmMix::SampleAlgorithmMix()
{    
}

SampleAlgorithmMix::~SampleAlgorithmMix()
{    
    UnInit();
}

void SampleAlgorithmMix::initConfigMap(){
    const char *configFile = "/usr/local/ev_sdk/config/algo_config.json";
    SDKLOG(INFO) << "Parsing configuration file: " << configFile;
    std::ifstream confIfs(configFile);
    if (confIfs.is_open())
    {
        size_t len = GetFileLen(confIfs);
        char *confStr = new char[len + 1];
        confIfs.read(confStr, len);
        confStr[len] = '\0';
        SDKLOG(INFO) << "Configs:"<<confStr;
        Json::Reader reader;
        Json::Value configValue;
        if( !reader.parse(confStr, configValue))
        {
            SDKLOG(ERROR) << "failed to parse config " << confStr;
        }

        Json::Value::Members mem = configValue.getMemberNames();    
        for(auto iter = mem.begin(); iter != mem.end(); iter++){
            SDKLOG(INFO) << *iter << "-" <<configValue[*iter].type();
        
            Configuration conf_;
            std::string tmp = configValue[*iter].toStyledString();
            SDKLOG(INFO) << "parse: "<< tmp;
            conf_.ParseAndUpdateArgs(tmp.c_str());
            mConfigMap[*iter] = conf_;
        }
    }else{
        SDKLOG(ERROR) << "not find " << configFile;
    }
}

JiErrorCode SampleAlgorithmMix::Init()
{
    // 从默认的配置文件读取相关配置参数

    
    mDetector0 = std::make_shared<SampleDetector>();
    mDetector1 = std::make_shared<SampleDetector>();


    std::string model_path = "/usr/local/ev_sdk/model/yolov5s_1684x_f16.bmodel";
    std::string class_file_path = "/usr/local/ev_sdk/coco.names";

    mDetector0->Init(model_path, class_file_path, 0.5);
    mDetector1->Init(model_path, class_file_path, 0.5);
    initConfigMap();
    return JISDK_RET_SUCCEED;
}

JiErrorCode SampleAlgorithmMix::UnInit(){
    if(mDetector0.get() != nullptr)
    {
        SDKLOG(INFO) << "uninit";
        mDetector0->UnInit();
        mDetector0.reset();
    }
        if(mDetector1.get() != nullptr)
    {
        SDKLOG(INFO) << "uninit";
        mDetector1->UnInit();
        mDetector1.reset();
    }
    return JISDK_RET_SUCCEED;
}

JiErrorCode SampleAlgorithmMix::UpdateConfig(const char *args)
{
    if (args == nullptr)
    {
        SDKLOG(ERROR) << "mConfig string is null ";
        return JISDK_RET_FAILED;
    }
    // mConfig.ParseAndUpdateArgs(args);
    for(auto & it : mConfigMap){
        it.second.ParseAndUpdateArgs(args);
    }
    return JISDK_RET_SUCCEED;
}

void SampleAlgorithmMix::setOutPut(std::vector<BoxInfo> &re,const JiAlgoParam& param,JiOutput &outInfos, int index) {

    std::vector<BoxInfo> re_filter;
    SDKLOG(INFO) << mConfigMap[param.algoId].algo_config.thresh;
    for(auto& box : re){
        if(box.score > mConfigMap[param.algoId].algo_config.thresh){
            re_filter.push_back(box);
        }
    }
    cv::Mat outImg = mOutputFrame.clone();
    if (mConfigMap[param.algoId].draw_roi_area && mConfigMap[param.algoId].draw_result && !mConfigMap[param.algoId].current_roi_orig_polygons.empty())
    {
        DrawPolygon(outImg,
                mConfigMap[param.algoId].current_roi_orig_polygons,
                cv::Scalar(mConfigMap[param.algoId].roi_color[0], mConfigMap[param.algoId].roi_color[1], mConfigMap[param.algoId].roi_color[2]),
                mConfigMap[param.algoId].roi_color[3],
                cv::LINE_AA,
                mConfigMap[param.algoId].roi_line_thickness,
                mConfigMap[param.algoId].roi_fill);
    }
    bool isNeedAlert = false;
    // 判断是否要要报警
    if (re_filter.size() > 0)
    {        
        isNeedAlert = true;
    }
    //并将检测到的在ROI内部的目标画到图上
    if(mConfigMap[param.algoId].draw_result)
    {
            for (auto & obj : re_filter)
            {
                std::stringstream ss;
                ss << obj.name;
                if (mConfigMap[param.algoId].draw_confidence)
                {
                    ss.precision(0);
                    ss << std::fixed << obj.score * 100 << "%";
                }
                cv::Rect rect = cv::Rect{int(obj.x1), int(obj.y1), int(obj.x2 - obj.x1), int(obj.y2 - obj.y1)};
                DrawRectText(outImg,
                                 rect, ss.str(),
                                 mConfigMap[param.algoId].target_rect_line_thickness,
                                 cv::LINE_AA,
                                 cv::Scalar(mConfigMap[param.algoId].target_rect_color[0], mConfigMap[param.algoId].target_rect_color[1], mConfigMap[param.algoId].target_rect_color[2]),
                                 mConfigMap[param.algoId].target_rect_color[3],
                                 mConfigMap[param.algoId].target_text_height,
                                 cv::Scalar(mConfigMap[param.algoId].text_foreground_color[0], mConfigMap[param.algoId].text_foreground_color[1], mConfigMap[param.algoId].text_foreground_color[2]),
                                 cv::Scalar(mConfigMap[param.algoId].text_background_color[0], mConfigMap[param.algoId].text_background_color[1], mConfigMap[param.algoId].text_background_color[2])
                                 ); 
            
            }
    }

    if (isNeedAlert && mConfigMap[param.algoId].draw_result && mConfigMap[param.algoId].draw_warning_text)
    {
        DrawText(outImg,
                 mConfigMap[param.algoId].warning_text_map[mConfigMap[param.algoId].language],
                 mConfigMap[param.algoId].warning_text_size,
                 cv::Scalar(mConfigMap[param.algoId].warning_text_foreground_color[0], mConfigMap[param.algoId].warning_text_foreground_color[1], mConfigMap[param.algoId].warning_text_foreground_color[2]),
                 cv::Scalar(mConfigMap[param.algoId].warning_text_background_color[0], mConfigMap[param.algoId].warning_text_background_color[1], mConfigMap[param.algoId].warning_text_background_color[2]),
                 mConfigMap[param.algoId].warning_text_left_top
                 );
    }
    mOutImageVec[index] = outImg;
    outInfos.outFrameCnt = 1;
    JiImageInfo j;
    j.nWidth = mOutImageVec[index].cols;
    j.nHeight = mOutImageVec[index].rows;
    j.nWidthStride = mOutImageVec[index].step;
    j.nHeightStride = mOutImageVec[index].rows;
    j.nFormat = JI_IMAGE_TYPE_BGR;
    j.nDataType = JI_UNSIGNED_CHAR;
    j.pData = mOutImageVec[index].data;

    mOutJiImageInfoVec[index] = j;
    // 将结果封装成json字符串
    bool jsonAlertCode = JSON_ALERT_FLAG_FALSE;
    if (isNeedAlert)
    {
        jsonAlertCode = JSON_ALERT_FLAG_TRUE;
    }
    Json::Value jRoot;
    Json::Value jAlgoValue;
    Json::Value jDetectValue;
    
    jAlgoValue[JSON_ALERT_FLAG_KEY] = jsonAlertCode;    
    jAlgoValue["target_info"].resize(0);
    for (auto &obj : re_filter)
    {        
        Json::Value jTmpValue;
        jTmpValue["x"] = int(obj.x1);
        jTmpValue["y"] = int(obj.y1);
        jTmpValue["width"] = int(obj.x2 - obj.x1);
        jTmpValue["height"] = int(obj.y2 - obj.y1);
        jTmpValue["name"] = obj.name;
        jTmpValue["confidence"] = obj.score;
        jAlgoValue["target_info"].append(jTmpValue);
    }
    jRoot["algorithm_data"] = jAlgoValue;

    //create model data
    jDetectValue["objects"].resize(0);

    for (auto &obj : re_filter)
    {
        Json::Value jTmpValue;
        jTmpValue["x"] = int(obj.x1);
        jTmpValue["y"] = int(obj.y1);
        jTmpValue["width"] = int(obj.x2 - obj.x1);
        jTmpValue["height"] = int(obj.y2 - obj.y1);
        jTmpValue["name"] = obj.name;
        jTmpValue["confidence"] = obj.score;
        jDetectValue["objects"].append(jTmpValue);
    }
    
    jRoot["model_data"] = jDetectValue;

    Json::StreamWriterBuilder writerBuilder;
    writerBuilder.settings_["precision"] = 2;
    writerBuilder.settings_["emitUTF8"] = true;    
    std::unique_ptr<Json::StreamWriter> jsonWriter(writerBuilder.newStreamWriter());
    std::ostringstream os;
    jsonWriter->write(jRoot, &os);
    mStrOutJson = os.str();    
    mOutJsonVec[index]=mStrOutJson;
    // 注意：JiEvent.code需要根据需要填充，切勿弄反
    if (isNeedAlert)
    {
        outInfos.event.code = JISDK_CODE_ALARM;
    }
    else
    {
        outInfos.event.code = JISDK_CODE_NORMAL;
    }
    outInfos.event.json = mOutJsonVec[index].c_str();    

    outInfos.pOutFrames = &mOutJiImageInfoVec[index];
    outInfos.algoName = param.algoName;
    outInfos.algoId = param.algoId;
}


JiErrorCode SampleAlgorithmMix::Process(bm_image &inFrame, const std::string &baseParams, 
const std::vector<JiAlgoParam> &params, std::vector<JiOutput> &outInfos){
    Json::Reader reader;

    Json::Value baseJson;

    reader.parse(baseParams.c_str(), baseJson);


    int mode = 0;
    cv::Mat tmp;
    cv::bmcv::toMAT(&inFrame, tmp);
    tmp.copyTo(mOutputFrame);
    std::vector<std::vector<BoxInfo>> re;
    re.resize(params.size());
    for(int i = 0; i < params.size(); i++) {
        auto p = params[i];
        if (p.algoId == "A_0"){
            if(mConfigMap.find(p.algoId) == mConfigMap.end()){
                SDKLOG(ERROR) << "config map not find " << p.algoId;
            }else{
                mConfigMap[p.algoId].ParseAndUpdateArgs(p.args.c_str());
            }
            std::vector<std::vector<BoxInfo>> detected_objs;
            mDetector0->ProcessImage({inFrame}, detected_objs);
            re[i] = detected_objs[0];
        }else if(p.algoId == "A_1"){
            if(mConfigMap.find(p.algoId) == mConfigMap.end()){
                SDKLOG(ERROR) << "config map not find " << p.algoId;
            }else{
                mConfigMap[p.algoId].ParseAndUpdateArgs(p.args.c_str());
            }
            std::vector<std::vector<BoxInfo>> detected_objs;
            mDetector1->ProcessImage({inFrame}, detected_objs);
            re[i] = detected_objs[0];
        }else{
            SDKLOG(ERROR) << "not match algo id";
        }
    }

    outInfos.resize(re.size());
    mOutJiImageInfoVec.resize(re.size());
    mOutJsonVec.resize(re.size());
    mOutImageVec.resize(re.size());
    for(int i = 0; i < re.size(); i ++){
        if (!params[i].args.empty()){
            mConfigMap[params[i].algoId].ParseAndUpdateArgs(params[i].args.c_str());
        }
        if (inFrame.width != mConfigMap[params[i].algoId].current_in_frame_size.width || inFrame.height != mConfigMap[params[i].algoId].current_in_frame_size.height)
        {
            SDKLOG(INFO)<<"Update ROI Info...";
            mConfigMap[params[i].algoId].UpdateROIInfo(inFrame.width, inFrame.height);
        }
        setOutPut(re[i], params[i], outInfos[i], i);
    }
    return JISDK_RET_SUCCEED;
}
